/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package co.edu.sd.jxta.socket;

import co.edu.sd.jxta.IPeer;
import co.edu.sd.jxta.IPeerUI;
import co.edu.sd.jxta.msn.MessageMSPFactory;
import co.edu.sd.jxta.service.ServiceType;
import java.io.File;
import java.io.IOException;
import java.net.Socket;
import java.util.ArrayList;
import java.util.List;
import net.jxta.endpoint.Message;
import net.jxta.peergroup.PeerGroup;
import net.jxta.protocol.PipeAdvertisement;
import net.jxta.socket.JxtaServerSocket;

/**
 *
 * @author SD
 */
public class P2PServerSoccket
        extends CommonSocket
        implements Runnable {

    private PipeAdvertisement adv;
    private JxtaServerSocket server;
    private Thread daemon;
    private PeerGroup oPeerGroup;

    public P2PServerSoccket(PipeAdvertisement adv, PeerGroup oPeerGroup, IPeer peer, IPeerUI peerUI) {
        this.adv = adv;
        this.peer = peer;
        this.oPeerGroup = oPeerGroup;
        this.peerUI = peerUI;
        try {
            initServer(oPeerGroup);
        } catch (IOException ex) {
            System.out.println("Starting ");
            ex.printStackTrace();
        }
        start();
    }

    public void start() {
        if (daemon == null) {
            daemon = new Thread(this);
            daemon.start();
        }
    }

    public void run() {
        while (true) {
            try {

                Socket oSocket = waitForConnection();
                setStreams(oSocket);
                peerUI.append("Starting " + adv.getName());

                //DH Security
                Message msg = MessageMSPFactory.createDefaultMessage(oPeerGroup.getPeerName(), adv.getName());
                MessageMSPFactory.printMessageStats(msg, true);

                try {
                    writeMessage(msg);
                    msg = readMessage();
                } catch (SecurityException se) {
                    output.flush();
                    output.close();
                    input.close();
                    oSocket.close();
                    this.peerUI.append("--session terminated for Security--");
                    continue;
                }

                MessageMSPFactory.printMessageStats(msg, true);

                this.peerUI.append("security Agreement completed");
                writeMessage(MessageMSPFactory.createUTCTimeMessage());
                switch (ServiceType.getTypeByID(adv.getName())) {
                    case FILE_SEARCH:
                        msg = readMessage();
                        writeMessage(peer.searchFile(msg));
                        msg = readMessage();
                        String nextstep = MessageMSPFactory.getStringFromMessage(msg, null, "" + MessageMSPFactory.Attributes.TAG_PROCESS_ID);
                        if (nextstep.equals(ServiceType.FILE_DOWNLOAD.getName())) {
                            String sFileName =
                                    new File(new File("MySharedFiles"), oPeerGroup.getPeerName() + "/" + MessageMSPFactory.getStringFromMessage(msg, null, "" + MessageMSPFactory.Attributes.TAG_FILE_NAME)).toString();
                            peerUI.append("getting file " + sFileName);
                            writeMessage(MessageMSPFactory.createFileUploadMessage(oPeerGroup.getPeerName(), sFileName));
                        }
                        break;
                    case FILE_DOWNLOAD:
                        msg = readMessage();
                        String sFileName =
                                new File(new File("MySharedFiles"), oPeerGroup.getPeerName() + "/" + MessageMSPFactory.getStringFromMessage(msg, null, "" + MessageMSPFactory.Attributes.TAG_FILE_NAME)).toString();

                        List messages = MessageMSPFactory.createFileDownloadMessagePieces(
                                oPeerGroup.getPeerName(), sFileName);
                        this.peerUI.append("download requires " + messages.size() + " slots");
                        writeMessage(MessageMSPFactory.createFileDownloadSessionMessage(
                                oPeerGroup.getPeerName(),
                                MessageMSPFactory.getStringFromMessage(msg, "" + MessageMSPFactory.Attributes.TAG_FILE_NAME),
                                messages.size()));
                        this.peerUI.append("starting to serve the file");
                        for (int i = 0; i < messages.size(); i++) {
                            Message current = (Message) messages.get(i);
                            this.peerUI.append(oPeerGroup.getPeerName() + "::sending piece " + i);
                            writeMessage(current);
                            try {
                                Thread.sleep(50L);
                            } catch (InterruptedException ex) {
                            }

                        }
                        peerUI.append("Finished sending file: " + sFileName);

                        break;
                    case RECOVER_FILE_DOWNLOAD:
                        msg = readMessage();
                        String sFileNameRecoverFileDownload =
                                new File(new File("MySharedFiles"), oPeerGroup.getPeerName() + "/" + MessageMSPFactory.getStringFromMessage(msg, null, "" + MessageMSPFactory.Attributes.TAG_FILE_NAME)).toString();

                        List messagesRecoverFileDownload = MessageMSPFactory.createFileDownloadMessagePieces(
                                oPeerGroup.getPeerName(), sFileNameRecoverFileDownload);

                        Message msgState = MessageMSPFactory.createFileRecoverDownloadSessionMessage(
                                oPeerGroup.getPeerName(),
                                MessageMSPFactory.getStringFromMessage(msg, "" + MessageMSPFactory.Attributes.TAG_FILE_NAME),
                                messagesRecoverFileDownload.size());

                        writeMessage(msgState);

                        int currentSegment = MessageMSPFactory.getIntegerFromMessage(msgState, "" + MessageMSPFactory.Attributes.TAG_CURRENT_PIECE);

                        this.peerUI.append("Download requires " + messagesRecoverFileDownload.size() + " slots");

                        this.peerUI.append("starting to serve the file");

                        for (int i = currentSegment; i < messagesRecoverFileDownload.size(); i++) {
                            Message current = (Message) messagesRecoverFileDownload.get(i);
                            this.peerUI.append(oPeerGroup.getPeerName() + "::sending piece " + i);
                            writeMessage(current);
                            try {
                                Thread.sleep(50L);
                            } catch (InterruptedException ex) {
                            }

                        }
                        //peerUI.append("getting file " + sFileName);
                        //writeMessage(MessageMSPFactory.createFileUploadMessage(oPeerGroup.getPeerName(), sFileName));
                        break;
                    case FILE_UPLOAD:
                        Message sessionmsg = readMessage();
                        int pieces = MessageMSPFactory.getIntegerFromMessage(sessionmsg, "" + MessageMSPFactory.Attributes.TAG_SESSION_PIECES);
                        int i = MessageMSPFactory.getIntegerFromMessage(sessionmsg, "" + MessageMSPFactory.Attributes.TAG_START_INDEX);
                        List<Message> msgs = new ArrayList<Message>();
                        while (i < pieces) {
                            msg = readMessage();
                            msgs.add(msg);
                            peerUI.append(oPeerGroup.getPeerName() + "::recieved piece: " + MessageMSPFactory.getIntegerFromMessage(msg, "" + MessageMSPFactory.Attributes.TAG_PIECE_ID));
                            i++;
                        }

                        peerUI.append("file: is uploaded" + MessageMSPFactory.getStringFromMessage(msg, null, "" + MessageMSPFactory.Attributes.TAG_FILE_NAME));
                        break;
                    case RECOVER_FILE_UPLOAD:
                        Message sessionmsgRecover = readMessage();
                        int piecesRecover = MessageMSPFactory.getIntegerFromMessage(sessionmsgRecover, "" + MessageMSPFactory.Attributes.TAG_SESSION_PIECES);
                        int currentPieceRecover = MessageMSPFactory.getIntegerFromMessage(sessionmsgRecover, "" + MessageMSPFactory.Attributes.TAG_CURRENT_PIECE);

                        List<Message> msgsRecover = new ArrayList<Message>();
                        while (currentPieceRecover < piecesRecover) {
                            msg = readMessage();
                            msgsRecover.add(msg);
                            peerUI.append(oPeerGroup.getPeerName() + "::recieved piece: " + MessageMSPFactory.getIntegerFromMessage(msg, "" + MessageMSPFactory.Attributes.TAG_PIECE_ID));
                            currentPieceRecover++;
                        }

                        peerUI.append("file: is uploaded" + MessageMSPFactory.getStringFromMessage(msg, null, "" + MessageMSPFactory.Attributes.TAG_FILE_NAME));
                        break;
                    case APP_EXEC:
                        msg = readMessage();
                        writeMessage(peer.execute(msg));
                        peerUI.append("running command: " + MessageMSPFactory.getStringFromMessage(msg, null, "" + MessageMSPFactory.Attributes.TAG_APP_COMMAND));
                        break;
                    case UTC_TIME:
                        msg = readMessage();
                        Message localTime = peer.time(msg);
                        writeMessage(localTime);
                        peerUI.append("digest: " + MessageMSPFactory.getUnsecureStringFromMessage(localTime, "" + MessageMSPFactory.Attributes.TAG_SEC_DIGEST));
                        peerUI.append("retreiving utc time: " + MessageMSPFactory.getStringFromMessage(localTime, "" + MessageMSPFactory.Attributes.TAG_TIME_OUTPUT));
                        break;

                }

                output.flush();
                output.close();
                input.close();
                oSocket.close();
                this.peerUI.append("--session finished--");
            } catch (ClassNotFoundException cnfe) {
                cnfe.printStackTrace();
            } catch (IOException ex) {
                ex.printStackTrace();
            }
        }
    }

    private Socket waitForConnection() throws IOException {
        peerUI.append("Waiting for connection " + adv.getName());
        Socket oSocket = server.accept();
        if (oSocket != null) {
            peerUI.append("New socket connection accepted");
        } else {
            peerUI.append("Error Creating socket");
        }
        return oSocket;
    }

    public void initServer(PeerGroup oPeerGroup) throws IOException {
        peerUI.append("Starting Socket Server " + adv.getName());
        server = new JxtaServerSocket(oPeerGroup, adv, 10);
        server.setSoTimeout(0);
    }
}
